<script setup lang="ts">
import { ref, onMounted } from 'vue'
import G6 from '@antv/g6'
import Mock from '@/views/mock/day6'
const mountNode = ref()
const nodeName = 'circle-animate'
const nodeBgName = 'background-animate'
const edgeName = 'circle-running'

const customFn = () => {
  G6.registerNode(
    nodeName,
    {
      /**
       * 绘制后的附加操作，默认没有任何操作
       * @param  {Object} cfg 节点的配置项
       * @param  {G.Group} group 图形分组，节点中的图形对象的容器
       */
      afterDraw(cfg: GraphOptions, group: Graph) {
        // 获取该节点上的第一个图形
        const shape = group.get('children')[0]
        shape.animate(
          (ratio) => {
            // 每一帧的操作，入参 ratio：这一帧的比例值（Number）。返回值：这一帧需要变化的参数集（Object）。
            // 先变大、再变小
            const diff = ratio <= 0.5 ? ratio * 10 : (1 - ratio) * 10
            let radius = cfg.size
            if (isNaN(radius)) radius = radius[0]
            // 返回这一帧需要变化的参数集，这里只包含了半径
            return {
              r: radius / 2 + diff
            }
          },
          {
            // 动画重复
            repeat: true,
            duration: 800,
            easing: 'easeCubic',
            callback: () => {
              // 动画循环 不会执行回调函数
            }
          }
        )
      }
    },
    'circle'
  ) // 该自定义节点继承了内置节点 'circle'，除了被复写的 afterDraw 方法外，其他按照 'circle' 里的函数执行。

  // Background Animation
  G6.registerNode(
    nodeBgName,
    {
      afterDraw(cfg, group) {
        const r = cfg.size / 2
        const back1 = group.addShape('circle', {
          zIndex: -3,
          attrs: {
            x: 0,
            y: 0,
            r,
            fill: cfg.color,
            opacity: 0.6
          },
          // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
          name: 'back1-shape'
        })
        const back2 = group.addShape('circle', {
          zIndex: -2,
          attrs: {
            x: 0,
            y: 0,
            r,
            fill: cfg.color,
            opacity: 0.6
          },
          // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
          name: 'back2-shape'
        })
        const back3 = group.addShape('circle', {
          zIndex: -1,
          attrs: {
            x: 0,
            y: 0,
            r,
            fill: cfg.color,
            opacity: 0.6
          },
          // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
          name: 'back3-shape'
        })
        group.sort() // Sort according to the zIndex
        back1.animate(
          {
            // Magnifying and disappearing
            r: r + 30,
            opacity: 0.1
          },
          {
            duration: 3000,
            easing: 'easeCubic',
            delay: 0,
            repeat: true // repeat
          }
        ) // no delay
        back2.animate(
          {
            // Magnifying and disappearing
            r: r + 10,
            opacity: 0.1
          },
          {
            duration: 3000,
            easing: 'easeCubic',
            delay: 1000,
            repeat: true // repeat
          }
        ) // 1s delay
        back3.animate(
          {
            // Magnifying and disappearing
            r: r + 20,
            opacity: 0.1
          },
          {
            duration: 3000,
            easing: 'easeCubic',
            delay: 2000,
            repeat: true // repeat
          }
        ) // 3s delay
      }
    },
    'circle'
  )

  G6.registerEdge(
    edgeName,
    {
      afterDraw(cfg, group) {
        // get the first shape in the group, it is the edge's path here=
        const shape = group.get('children')[0]
        // the start position of the edge's path
        const startPoint = shape.getPoint(0)

        // add red circle shape
        const circle = group.addShape('circle', {
          attrs: {
            x: startPoint.x,
            y: startPoint.y,
            fill: '#1890ff',
            r: 3
          },
          // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
          name: 'circle-shape'
        })

        // animation for the red circle
        circle.animate(
          (ratio) => {
            // the operations in each frame. Ratio ranges from 0 to 1 indicating the prograss of the animation. Returns the modified configurations
            // get the position on the edge according to the ratio
            const tmpPoint = shape.getPoint(ratio)
            // returns the modified configurations here, x and y here
            return {
              x: tmpPoint.x,
              y: tmpPoint.y
            }
          },
          {
            repeat: true, // Whether executes the animation repeatly
            duration: 1000 // the duration for executing once
          }
        )
      }
    },
    'line'
  )
}

customFn()

const init = () => {
  // 定义数据、实例化图、加载数据、渲染图
  const graph = new G6.Graph({
    container: mountNode.value,
    width: document.documentElement.clientWidth * 0.6,
    height: document.documentElement.clientHeight * 0.7,
    defaultNode: {
      type: nodeName,
      size: 80,
      style: {
        cursor: 'pointer'
      },
      labelCfg: {
        style: {
          cursor: 'pointer'
        }
      }
    },
    animate: true,
    groupByTypes: false,
    animateCfg: {
      duration: 1000 // Number，一次动画的时长
    },
    modes: {
      default: ['drag-node']
    },
    defaultEdge: {
      type: edgeName,
      style: {
        lineWidth: 2,
        endArrow: {
          path: G6.Arrow.vee(5, 7, 0)
        }
      }
    }
  })
  graph.data(Mock) //  加载数据
  graph.render() // 渲染图

  graph.on('click', (ev) => {
    const position = ev.item.getModel()

    // 平移动画
    // graph.updateItem(
    //   position.id,
    //   {
    //     x: position.x + 100, // 在 x 轴上平移 100
    //     y: position.y // y 轴位置保持不变
    //   },
    //   {
    //     duration: 1000, // 动画持续时间为 1 秒
    //     easing: 'easeLinear' // 缓动函数
    //   }
    // )
    // 渐变动画
    if (position.id === 'node4') {
      graph.updateItem(position.id, {
        type: nodeBgName
      })
      graph.refreshItem(position.id)
    }
  })
  // 鼠标悬停时触发交互动画
  graph.on('node:mouseenter', (ev) => {
    const position = ev.item.getModel()

    graph.updateItem(position.id, {
      style: {
        fill: '#40a9ff' // 鼠标悬停时节点颜色变色
      },
      labelCfg: {
        style: {
          fill: 'white' // 鼠标悬停时节点文本颜色变为白色
        }
      },
      animate: true // 启用动画效果
    })
  })

  graph.on('node:mouseleave', (ev) => {
    const position = ev.item.getModel()

    graph.updateItem(position.id, {
      style: {
        fill: '#bae7ff' // 鼠标离开时节点颜色
      },
      labelCfg: {
        style: {
          fill: 'red' // 鼠标离开时节点文本颜色红色
        }
      },
      animate: true // 启用动画效果
    })
  })
}

onMounted(() => {
  init()
})
</script>
<template>
  <div ref="mountNode" class="container" />
</template>

<style lang="scss" scoped>
.container {
  border: 4px solid #5b8ff9;
}
</style>
